//////////////////////////////////////////////////////////////////////////
#pragma once


#include <Math/Point3f.hpp>


namespace Math
{
	//////////////////////////////////////////////////////////////////////////
	inline Vector3f::Vector3f()
	{
	}


	inline Vector3f::Vector3f( const Vector3f& rhs )
	:	mX( rhs.mX )
	,	mY( rhs.mY )
	,	mZ( rhs.mZ )
	{
	}


	inline Vector3f::Vector3f( const Point3f& rhs )
	:	mX( rhs.X() )
	,	mY( rhs.Y() )
	,	mZ( rhs.Z() )
	{
	}


	inline Vector3f::Vector3f( float x, float y, float z )
	:	mX( x )
	,	mY( y )
	,	mZ( z )
	{
	}


	//////////////////////////////////////////////////////////////////////////
	inline float Vector3f::X() const
	{
		return mX;
	}


	inline float Vector3f::Y() const
	{
		return mY;
	}


	inline float Vector3f::Z() const
	{
		return mZ;
	}


	inline void Vector3f::X( float x )
	{
		mX = x;
	}


	inline void Vector3f::Y( float y )
	{
		mY = y;
	}


	inline void Vector3f::Z( float z )
	{
		mZ = z;
	}


	//////////////////////////////////////////////////////////////////////////
	inline float Vector3f::operator ()( size_t idx ) const
	{
		assert( idx <= 3 );
		return (&mX)[ idx ];
	}


	inline void Vector3f::operator ()( size_t idx, float v )
	{
		assert( idx <= 3 );
		(&mX)[ idx ] = v;
	}


	//////////////////////////////////////////////////////////////////////////
	inline bool Vector3f::operator ==( const Vector3f& rhs ) const
	{
		return mX==rhs.mX && mY==rhs.mY && mZ==rhs.mZ;
	}


	inline bool Vector3f::operator !=( const Vector3f& rhs ) const
	{
		return !((*this)==rhs);
	}


	//////////////////////////////////////////////////////////////////////////
	inline void Vector3f::Set( float x, float y, float z )
	{
		mX	=	x;
		mY	=	y;
		mZ	=	z;
	}


	inline const Vector3f& Vector3f::operator =( const Vector3f& rhs )
	{
		mX	=	rhs.mX;
		mY	=	rhs.mY;
		mZ	=	rhs.mZ;
		return *this;
	}


	//////////////////////////////////////////////////////////////////////////
	inline Vector3f Vector3f::operator -() const							{return Vector3f( -mX, -mY, -mZ );}
	inline Vector3f Vector3f::operator -( const Vector3f& rhs ) const		{return Vector3f( mX-rhs.mX, mY-rhs.mY, mZ-rhs.mZ );}
	inline Vector3f Vector3f::operator +( const Vector3f& rhs ) const		{return Vector3f( mX+rhs.mX, mY+rhs.mY, mZ+rhs.mZ );}
	inline float Vector3f::operator *( const Vector3f& rhs ) const		{return( mX*rhs.mX + mY*rhs.mY + mZ*rhs.mZ );}
	inline Vector3f Vector3f::operator *( float rhs ) const				{return Vector3f( mX*rhs, mY*rhs, mZ*rhs );}
	inline Vector3f Vector3f::operator /( float rhs ) const				{return Vector3f( mX/rhs, mY/rhs, mZ/rhs );}
	inline void Vector3f::operator -=( const Vector3f& rhs )
	{
		mX	-=	rhs.mX;
		mY	-=	rhs.mY;
		mZ	-=	rhs.mZ;
	}
	inline void Vector3f::operator +=( const Vector3f& rhs )
	{
		mX	+=	rhs.mX;
		mY	+=	rhs.mY;
		mZ	+=	rhs.mZ;
	}
	inline void Vector3f::operator *=( float rhs )
	{
		mX	*=	rhs;
		mY	*=	rhs;
		mZ	*=	rhs;
	}
	inline void Vector3f::operator /=( float rhs )
	{
		mX	/=	rhs;
		mY	/=	rhs;
		mZ	/=	rhs;
	}


	//////////////////////////////////////////////////////////////////////////
	inline void Vector3f::Normalize()
	{
		*this /= sqrt( *this * *this );
	}


	inline void Vector3f::Cross( const Vector3f& rhs )
	{
		*this	=	Vector3f(
			mY * rhs.mZ - mZ * rhs.mY,
			mZ * rhs.mX - mX * rhs.mZ,
			mX * rhs.mY - mY * rhs.mX
			);
	}


	inline bool Vector3f::IsValid() const
	{
		return (mX*0.0f)==0.0f	&&	(mY*0.0f)==0.0f	&&	(mZ*0.0f)==0.0f;
	}


	//////////////////////////////////////////////////////////////////////////
	inline const Vector3f Vector3f::Zero()										{return Vector3f( 0.0f, 0.0f, 0.0f );}
	inline const Vector3f Vector3f::XAxis()										{return Vector3f( 1.0f, 0.0f, 0.0f );}
	inline const Vector3f Vector3f::YAxis()										{return Vector3f( 0.0f, 1.0f, 0.0f );}
	inline const Vector3f Vector3f::ZAxis()										{return Vector3f( 0.0f, 0.0f, 1.0f );}


	//////////////////////////////////////////////////////////////////////////
	inline Vector3f					operator *( float lhs, const Vector3f& rhs )
	{
		return rhs * lhs;
	}


	//////////////////////////////////////////////////////////////////////////
	inline float					Dot( const Vector3f& lhs, const Vector3f& rhs )
	{
		return lhs * rhs;
	}

	inline Vector3f					Normalize( const Vector3f& rhs )
	{
		Vector3f	result( rhs );
		result.Normalize();
		return result;
	}

	inline Vector3f					Cross( const Vector3f& lhs, const Vector3f& rhs )
	{
		Vector3f	result( lhs );
		result.Cross( rhs );
		return result;
	}

	inline float					Magnitude( const Vector3f& lhs )
	{
		return std::sqrt( lhs * lhs );
	}

	inline float					MagnitudeSquared( const Vector3f& lhs )
	{
		return lhs*lhs;
	}

	inline Vector3f					Multiply( const Vector3f& lhs, const Vector3f& rhs )
	{
		return Vector3f(
			lhs.X() * rhs.X(),
			lhs.Y() * rhs.Y(),
			lhs.Z() * rhs.Z()	);
	}

	inline Vector3f					Reflect( const Vector3f& v, const Vector3f& axis )
	{
		const float	dot2	= axis * v * 2.0f;
		return( dot2*axis-v );
	}

	inline Vector3f					Refract( const Vector3f& i, const Vector3f& n, float eta )
	{
		float					cosI	= Dot( -n, i );
		float					sinT2	= eta*eta * (1.0f - cosI * cosI);
		if( sinT2 > 1.0f )
			return Vector3f::Zero();
		return eta*i - (eta + std::sqrt( 1.0f - sinT2 )) * -n;
	}
}
